﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace YasharEl.Infrastructure.ServiceModel.Messaging
{
    public class MessageHeaderInformationExtractor : IDispatchMessageInspector, IServiceBehavior
    {
        #region Fields

        private readonly List<MessageHeaderConsumerInformation> messageHeaderEntries = new List<MessageHeaderConsumerInformation>();

        #endregion

        #region Constructors

        public MessageHeaderInformationExtractor(List<MessageHeaderConsumerInformation> entries)
        {
            if (entries != null)
            {
                messageHeaderEntries = entries;
            }
        }

        #endregion

        #region IDispatchMessageInspector Implementations

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // TODO : Validate all possible problems and throw exception for each case

            if (request != null)
            {
                foreach (MessageHeaderConsumerInformation messageHeaderEntry in messageHeaderEntries)
                {
                    int headerIndex = request.Headers.FindHeader(
                        messageHeaderEntry.MessageHeaderKey,
                        messageHeaderEntry.MessageHeaderNamespace
                    );

                    if (headerIndex == -1)
                    {
                        throw new MessageHeaderException(
                            string.Format("Expected message header with name {0} and namespace {1} not found.", messageHeaderEntry.MessageHeaderKey, messageHeaderEntry.MessageHeaderNamespace)
                        );
                    }

                    XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerIndex);

                    if( reader != null )
                    {
                        object serializedContent = reader.ReadElementContentAsObject(
                            messageHeaderEntry.MessageHeaderKey,
                            messageHeaderEntry.MessageHeaderNamespace);

                        if (serializedContent != null)
                        {
                            XmlSerializer deserializer = new XmlSerializer(typeof(MessageHeaderValueWrapper));
                            TextReader textReader = new StringReader(serializedContent.ToString());
                            MessageHeaderValueWrapper valueWrapper = (MessageHeaderValueWrapper)deserializer.Deserialize(textReader);

                            textReader.Close();

                            messageHeaderEntry.ValueConsumer.ConsumeValue(valueWrapper.RealValue);
                        }
                    }
                }
            }

            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }

        #endregion

        #region IServiceBehavior Implementations

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
                {
                    epDisp.DispatchRuntime.MessageInspectors.Add(this);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }
}
